//  IDVI 1.0 source copyright 1996 Garth A. Dickie
//
//  This source is free for non-commercial use.  No warranty, etc.
//  Please acknowledge reuse by including the line:
//
//  "Based in part on IDVI 1.0 source copyright 1996 Garth A. Dickie"
//
//  in your documentation and source code.  For commercial use or
//  distribution, please contact the author.  Please also send
//  questions, comments, bug reports, or fixes.
//
//  Best Regards,
//  Garth A. Dickie
//  dickie@elastic.avid.com

package ibook.v10.idvi.display;

import java.awt.Graphics;
import java.awt.Color;
import java.util.Hashtable;

//  This is a featherweight class in the View heierarchy.
//
//  There is at most one instance of the RuleView class for each 4-tuple
//  (ColorScheme, scale, topIntensity, rightIntensity).  This is implemented
//  as at most one for each triple (ScaledColorScheme, topIntensity,
//  rightIntensity), where there is at most one ScaledColorScheme for each
//  pair (ColorScheme, scale).

final class RuleView extends View {
    private static Hashtable    cache = new Hashtable( );
    private static RuleView     key = new RuleView( );

    static synchronized RuleView getRuleView( ScaledColorScheme colorScheme, int width, int height, int scale ) {
        int top = height % scale;
        int right = width % scale;

        if( top == 0 ) top = scale;
        if( right == 0 ) right = scale;

        key.colorScheme = colorScheme;
        key.topIntensity = top * scale;
        key.rightIntensity = right * scale;

        RuleView result = ( RuleView ) cache.get( key );

        if( result == null ) {
            key.topRightIntensity = top * right;
            result = key.init( );
            cache.put( key, result );
            key = new RuleView( );
        }

        return result;
    }




    //  Instance variables and methods.
    //
    //  Since the constructor is private, the only way to construct RuleView
    //  objects is via the class method getRuleView( ) above.  That method
    //  garauntees that init( ) is called before the object is returned.

    private ScaledColorScheme   colorScheme;
    private int                 topIntensity;
    private int                 rightIntensity;
    private int                 topRightIntensity;

    private Color[ ]            solidColor;
    private Color[ ]            topColor;
    private Color[ ]            rightColor;
    private Color[ ]            topRightColor;

    private RuleView( ) {
    }

    private RuleView init( ) {
        solidColor    = new Color[ ScaledColorScheme.kIndexCount ];
        topColor      = new Color[ ScaledColorScheme.kIndexCount ];
        rightColor    = new Color[ ScaledColorScheme.kIndexCount ];
        topRightColor = new Color[ ScaledColorScheme.kIndexCount ];

        return this;
    }

    int getBounds( DVIRectangle bounds, int scale, int x, int y, Block block ) {
        (( RuleBlock ) block ).getBounds( bounds, scale, x, y );
        return 0;
    }

    //  We avoid synchronization almost all of the time by keeping an array of
    //  pre-computed Colors.  If the entry that we need is non-null, we just use
    //  it; otherwise we need a synchronized method to initialize the entry.

    void paint( Graphics g, int color, DVIRectangle bounds, DVIRectangle clip, int yOffset ) {
        int solidWidth = bounds.right - bounds.left - 1;
        int solidHeight = bounds.bottom - bounds.top - 1;

        boolean wide = solidWidth != 0;
        boolean tall = solidHeight != 0;

        if( wide && tall ) {
            if( solidColor[ color ] == null )
                prepareColor( solidColor, color );

            g.setColor( solidColor[ color ] );
            g.fillRect( bounds.left, bounds.top + 1, solidWidth, solidHeight );
        }

        if( wide ) {
            if( topColor[ color ] == null )
                prepareColor( topColor, color, topIntensity );

            g.setColor( topColor[ color ] );
            g.fillRect( bounds.left, bounds.top, solidWidth, 1 );
        }

        if( tall ) {
            if( rightColor[ color ] == null )
                prepareColor( rightColor, color, rightIntensity );

            g.setColor( rightColor[ color ] );
            g.fillRect( bounds.right - 1, bounds.top + 1, 1, solidHeight );
        }

        if( topRightColor[ color ] == null )
            prepareColor( topRightColor, color, topRightIntensity );

        g.setColor( topRightColor[ color ] );
        g.fillRect( bounds.right - 1, bounds.top, 1, 1 );
    }

    private synchronized void prepareColor( Color[ ] target, int index, int intensity ) {
        if( target[ index ] == null )
            target[ index ] = colorScheme.getColor( index, intensity );
    }

    private synchronized void prepareColor( Color[ ] target, int index ) {
        if( target[ index ] == null )
            target[ index ] = colorScheme.getForegroundColor( index );
    }




    public int hashCode( ) {
        return colorScheme.hashCode( ) ^ topIntensity ^ ( rightIntensity << 8 );
    }

    public boolean equals( Object object ) {
        return object instanceof RuleView && equals(( RuleView ) object );
    }

    //  We can compare ScaledColorScheme objects for equality using == rather
    //  than equals( ), because they are unique in the same way that RuleView
    //  objects are unique.

    public boolean equals( RuleView key ) {
        return
            key.colorScheme    == colorScheme &&
            key.topIntensity   == topIntensity &&
            key.rightIntensity == rightIntensity;
    }
}
